import pika
from pika import spec
import logging


class RabbitmqConfig:
    def __init__(self, server, port, username, password, vhost, exchange, exchange_type, routing_key, queue_name):
        self.server = server
        self.port = port
        self.username = username
        self.password = password
        self.vhost = vhost
        self.exchange = exchange
        self.exchange_type = exchange_type
        self.routing_key = routing_key
        self.queue_name = queue_name


class Rabbitmq:
    def __init__(self, rabbitmq_config):
        self.logger = logging.getLogger("rabbitmq")
        self.rabbitmq_config = rabbitmq_config
        self.credentials = pika.PlainCredentials(self.rabbitmq_config.username, self.rabbitmq_config.password)
        self.conn_params = pika.ConnectionParameters(self.rabbitmq_config.server,
                                                     virtual_host=self.rabbitmq_config.vhost,
                                                     port=int(self.rabbitmq_config.port),
                                                     credentials=self.credentials)
        self.conn_broker = pika.BlockingConnection(self.conn_params)

        self.channel = self.conn_broker.channel()
        self.channel.exchange_declare(exchange=self.rabbitmq_config.exchange,
                                      type=self.rabbitmq_config.exchange_type,
                                      passive=False,
                                      durable=True,
                                      auto_delete=False)

    def send_msg(self, msg, confirm=False):

        msg_ids = []

        def confirm_handler(frame):
            if type(frame.method) == spec.Confirm.SelectOk:
                self.logger.info("Channel in 'confirm' mode.")
            elif type(frame.method) == spec.Basic.Nack:
                if frame.method.delivery_tag in msg_ids:
                    self.logger.info('Message lost!')
            elif type(frame.method) == spec.Basic.Ack:
                if frame.method.delivery_tag in msg_ids:
                    self.logger.info('confirm received!')
                    msg_ids.remove(frame.method.delivery_tag)

        if confirm:
            self.channel.confirm_delivery(callback=confirm_handler)

        msg_props = pika.BasicProperties()
        msg_props.content_type = "text/plain"
        self.channel.basic_publish(body=msg,
                                   exchange=self.rabbitmq_config.exchange,
                                   properties=msg_props,
                                   routing_key=self.rabbitmq_config.routing_key
                                   )
        msg_ids.append(len(msg_ids) + 1)
        self.channel.close()

    def receive_msg(self):
        self.logger.info('start receive_msg')
        self.channel.queue_declare(queue=self.rabbitmq_config.queue_name, durable=True)
        self.channel.queue_bind(queue=self.rabbitmq_config.queue_name,
                                exchange=self.rabbitmq_config.exchange,
                                routing_key=self.rabbitmq_config.routing_key)

        def msg_consumer(channel, method, header, body):

            channel.basic_ack(delivery_tag=method.delivery_tag)
            if body.decode("utf-8") == 'quit':
                channel.basic_cancel(consumer_tag="hello-consumer")
                channel.stop_consuming()
            else:
                self.logger.info(body)
            return

        self.channel.basic_consume(msg_consumer, queue=self.rabbitmq_config.queue_name, consumer_tag="hello-consumer")
        self.channel.start_consuming()
